/*
 * This file is part of "IdeWiki" project.
 *
 * Copyright (c) 2008 Hendrik Ebel
 * All Rights Reserved.
 *
 * Please visit http://code.google.com/p/idewiki for updates and contact.
 *
 * --LICENSE NOTICE--
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 3
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; If not, see <http://www.gnu.org/licenses/>.
 * --LICENSE NOTICE--
 */
package ws.ebel.idewiki.editors;

import java.util.ArrayList;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.internal.adaptor.EclipseEnvironmentInfo;
import org.eclipse.jdt.core.IJavaModel;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.swt.graphics.Point;

/**
 * The Class SnipSnapMacroProcessor.
 */
public class SnipSnapMacroProcessor implements IContentAssistProcessor {

    /** The Constant IDE_MACRO_PREFIX. */
    private static final String IDE_MACRO_PREFIX = "{ide:p=";

    // Proposal part before cursor
    /** The Constant MACRO_START_NAME. */
    private final static String[] MACRO_START_NAME = new String[] { IDE_MACRO_PREFIX, "{code}", "{graph}", "{image:",
            "{link:", "{quote}", "{table}", "{mks:" };

    /** The Constant MACRO_END_NAME. */
    private final static String[] MACRO_END_NAME = new String[] { "}", "{code}", "{graph}", "}", "}", "{quote}", "{table}", "}" };

    /** The Constant STYLETAGS. */
    private final static String[] STYLETAGS = new String[] { "__", "~~", "--", "{code}" };
    
    /** The Constant STYLELABELS. */
    private final static String[] STYLELABELS = new String[] { "bold", "italic", "strikethrough", "code" };
    

    /**
     * Compute completion proposals.
     * 
     * @param viewer the viewer
     * @param offset the offset
     * 
     * @return the I completion proposal[]
     */
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
        IDocument doc = viewer.getDocument();
        ArrayList<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
        // Retrieve current selection range
        Point selectedRange = viewer.getSelectedRange();

        if (selectedRange.y > 0) {
            try {
                // Retrieve selected text
                String text = doc.get(selectedRange.x, selectedRange.y);
                // Compute completion proposals
                result.addAll(computeStyleProposals(text, selectedRange));
            } catch (BadLocationException e) {
                // do nothing
            }
        } else {

            // Retrieve qualifier 
            int subOffset = getOsDependOffset();
            String qualifier = getQualifier(doc, offset + subOffset);

            if (qualifier.startsWith(IDE_MACRO_PREFIX) && !qualifier.contains("|c=")) {
                result.addAll(computeProjectProposal(qualifier, offset + subOffset));
            } else if (qualifier.startsWith(IDE_MACRO_PREFIX) && qualifier.contains("|c=")) {
                result.addAll(computeJavaProposal(viewer, qualifier, offset + subOffset));
            } 
            else {
                // Compute completion proposals
                result.addAll(computeStructureProposals(qualifier, offset + subOffset));
            }
        }

        return (ICompletionProposal[]) result.toArray(new ICompletionProposal[result.size()]);
    }

	/**
	 * Gets the os depend offset.
	 * 
	 * @return the os depend offset
	 */
	private int getOsDependOffset() {
		 // =!= offset on mac osx "- 1"
		 int result = 0;
		 if (EclipseEnvironmentInfo.getDefault().getOS().equals("macosx")) {
			 result = -1;
		 }
		return result;
	}

    /**
     * Compute java proposal.
     * 
     * @param viewer the viewer
     * @param qualifier the qualifier
     * @param offset the offset
     * 
     * @return the array list completion proposal
     */
    private ArrayList<ICompletionProposal> computeJavaProposal(ITextViewer viewer, String qualifier, int offset) {
        ArrayList<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
        IJavaProject project = getJavaProject(qualifier);

        if (project != null) {
            JavaCompletionProcessor javaProcessor = new JavaCompletionProcessor();
            try {
                result.addAll(javaProcessor.getProposals(project, viewer, offset + 1));
            } catch (BadLocationException e) {
                // do nothing
            }
        }

        return result;
    }

    /**
     * Gets the java project.
     * 
     * @param qualifier the qualifier
     * 
     * @return the java project
     */
    private IJavaProject getJavaProject(String qualifier) {
        IJavaProject result = null;

        if (qualifier != null && qualifier.indexOf("|") != -1) {
            String projectName = qualifier.substring(IDE_MACRO_PREFIX.length(), qualifier.indexOf("|"));

            IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
            IJavaModel model = JavaCore.create(root);
            result = model.getJavaProject(projectName);
        }
        return result;
    }

    /**
     * Compute project proposal.
     * 
     * @param qualifier the qualifier
     * @param offset the offset
     * 
     * @return the array list< I completion proposal>
     */
    private ArrayList<ICompletionProposal> computeProjectProposal(String qualifier, int offset) {
        ArrayList<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
        IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
        IProject[] projects = root.getProjects();
        int qlen = qualifier.length();
        String projectPrefix = getProjectNamePrefix(qualifier);

        for (int i = 0; i < projects.length; i++) {
            IProject project = projects[i];
            String projectName = project.getName();

            if (projectName.startsWith(projectPrefix)) {
                // Yes -- compute whole proposal text
                String text = IDE_MACRO_PREFIX + projectName + "|c=";

                // Derive cursor position
                int cursor = text.length();

                // Construct proposal
                ICompletionProposal proposal = new CompletionProposal(text, offset - qlen, qlen, cursor);
                result.add(proposal);
            }
        }
        return result;
    }

    /**
     * Gets the project name prefix.
     * 
     * @param qualifier the qualifier
     * 
     * @return the project name prefix
     */
    private String getProjectNamePrefix(String qualifier) {
        String result = "";
        if (qualifier != null && qualifier.startsWith(IDE_MACRO_PREFIX)) {
            result = qualifier.substring(IDE_MACRO_PREFIX.length(), qualifier.length());
        }

        return result;
    }

    /**
     * Compute style proposals.
     * 
     * @param text the text
     * @param selectedRange the selected range
     * 
     * @return the array list< I completion proposal>
     */
    private ArrayList<ICompletionProposal> computeStyleProposals(String text, Point selectedRange) {
        ArrayList<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
        // Loop through all styles
        for (int i = 0; i < STYLETAGS.length; i++) {
            String tag = STYLETAGS[i];

            // Compute replacement text
            String replacement = tag + text + tag;

            // Derive cursor position
            int cursor = tag.length();

            // Compute a suitable context information
            IContextInformation contextInfo = new ContextInformation(null, STYLELABELS[i] + " Style");

            // Construct proposal
            CompletionProposal proposal = new CompletionProposal(replacement, selectedRange.x, selectedRange.y, cursor, null, STYLELABELS[i], contextInfo, replacement);

            // and add to result list
            result.add(proposal);
        }
        return result;
    }

    /**
     * Compute structure proposals.
     * 
     * @param qualifier the qualifier
     * @param offset the offset
     * 
     * @return the array list< I completion proposal>
     */
    private ArrayList<ICompletionProposal> computeStructureProposals(String qualifier, int offset) {
        ArrayList<ICompletionProposal> result = new ArrayList<ICompletionProposal>();
        int qlen = qualifier.length();

        // Loop through all proposals
        for (int i = 0; i < MACRO_START_NAME.length; i++) {
            String startTag = MACRO_START_NAME[i];

            // Check if proposal matches qualifier
            if (startTag.startsWith(qualifier) && qualifier.length() != 0) {

                // Yes -- compute whole proposal text
                String text = startTag + MACRO_END_NAME[i];

                // Derive cursor position
                int cursor = startTag.length();

                // Construct proposal
                CompletionProposal proposal = new CompletionProposal(text, offset - qlen, qlen, cursor);

                // and add to result list
                result.add(proposal);
            }
        }

        return result;
    }

    /**
     * Gets the qualifier.
     * 
     * @param doc the doc
     * @param offset the offset
     * 
     * @return the qualifier
     */
    private String getQualifier(IDocument doc, int offset) {
        // Use string buffer to collect characters
        StringBuffer buf = new StringBuffer();
        while (true) {
            try {

                // Read character backwards
                char c = doc.getChar(--offset);

                // This was not the start of a tag
                if (c == '}' || Character.isWhitespace(c)) {
                    return "";
                }

                // Collect character
                buf.append(c);

                // Start of tag. Return qualifier
                if (c == '{') {
                    return buf.reverse().toString();
                }

            } catch (BadLocationException e) {
                // Document start reached, no tag found
                return "";
            }
        }
    }

    /**
     * Compute context information.
     * 
     * @param viewer the viewer
     * @param offset the offset
     * 
     * @return the I context information[]
     */
    public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Gets the completion proposal auto activation characters.
     * 
     * @return the completion proposal auto activation characters
     */
    public char[] getCompletionProposalAutoActivationCharacters() {
        // TODO Auto-generated method stub
        return new char[] { '{' };
    }

    /**
     * Gets the context information auto activation characters.
     * 
     * @return the context information auto activation characters
     */
    public char[] getContextInformationAutoActivationCharacters() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Gets the context information validator.
     * 
     * @return the context information validator
     */
    public IContextInformationValidator getContextInformationValidator() {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * Gets the error message.
     * 
     * @return the error message
     */
    public String getErrorMessage() {
        // TODO Auto-generated method stub
        return null;
    }

}
